package org.jeecg.modules.front.interceptor;

import cn.hutool.crypto.digest.MD5;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.Claim;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.jeecg.modules.annotation.PassToken;
import org.jeecg.modules.annotation.UserLoginToken;
import org.jeecg.modules.front.cache.RedisCacheUtils;
import org.jeecg.modules.front.consts.UserType;
import org.jeecg.modules.front.consts.WebConst;
import org.jeecg.modules.front.entity.resp.BusUserResp;
import org.jeecg.modules.front.service.BusUserService;
import org.jeecg.modules.front.util.JWTTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.util.Locale;
import java.util.Map;


@Slf4j
//@Component
public class AuthenticationInterceptor implements HandlerInterceptor {

    @Value("${jwt.secret}")
    private String SECRET;

    @Autowired
    private BusUserService userService;

    @Autowired
    private RedisCacheUtils redisCacheUtils;

    public static String getIpAddr(HttpServletRequest request) {
        String ipAddress = request.getHeader("x-forwarded-for");
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
                //根据网卡取本机配置的IP
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                ipAddress = inet.getHostAddress();
            }
        }
        //对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
        if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15
            if (ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        String ip = getIpAddr(httpServletRequest);
        //每次请求清除线程变量中的用户信息
        MemberContext.removeMemberSession();
        UserContext.removeUserSession();
        // 国际化
        LocaleContextHolder.resetLocaleContext();

        // 从 session中获取，自动登录的时候会重置session中的 token
        String token = (String) httpServletRequest.getSession().getAttribute(WebConst.SESSION_TOKEN);
        // 如果http请求头没有token则尝试从session中获取
        if (StringUtils.isEmpty(token)) {
            token = httpServletRequest.getHeader("token");
        }

        // 国际化
        String language = httpServletRequest.getHeader(WebConst.SESSION_LANGUAGE);
        // 保存语言
        if (StringUtils.equals(WebConst.LanguageEnum.CHINESE.getCode(), language)) {
            LocaleContextHolder.setLocale(Locale.CHINESE);
        }
        else if (StringUtils.equals(WebConst.LanguageEnum.ENGLISH.getCode(), language)) {
            LocaleContextHolder.setLocale(Locale.ENGLISH);
        }

        // 如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        //检查是否有passtoken注释，有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }

        //检查有没有需要用户权限的注解
        String id = "";
        String sessionId = "";
        String type = "";
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            log.info("校验token = {}", token);
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                // 执行认证
                if (token == null || "".equals(token) || "null".equals(token) ) {
                    throw new AuthenticationException("token为空");
                }
                BusUserResp resp = (BusUserResp) redisCacheUtils.getCacheObject(token);
                if (resp == null) {
                    throw new AuthenticationException("登录过期或账号在其他地方登录，请重新登录");
                } else {
                    UserContext.setSysUser(resp);
                    UserContext.setUserId(id);
                    UserContext.setSessionId(sessionId);
                }
                // 如果需要验证签名，且签名校验不通过
//                if (isCheckSign() && !doCheckSign(httpServletRequest.getHeader(SIGN_KEY_PARAM_NAME))) {
//                    throw new AuthenticationException("非法请求，请检查参数");
//                }
//                if (!checkSign(httpServletRequest)) {
//                    throw new AuthenticationException("非法请求，请检查参数");
//                }//TODO 前端添加签名跟时间戳后放开

                try {
                    Map<String, Claim> claims = JWT.decode(token).getClaims();
                    id = claims.get("id").asString();
                    sessionId = claims.get("sessionId").asString();
                    type = claims.get("type").asString();
                    int exp = claims.get("exp").asInt();
                    int iat = claims.get("iat").asInt();
                    String selfToken = claims.get("selfToken").asString();
                    String hash = "id=" + id + "&sessionId=" + sessionId + "&secret=" + SECRET + "&exp=" + String.valueOf(exp) + "&iat=" + String.valueOf(iat);
                    MD5 md5 = new MD5();
                    md5.digest(hash, "");
                    if (!selfToken.equals(md5.digestHex(hash))) {
                        throw new AuthenticationException("校验不通过，无效token，请重新登录");
                    }

                    /*if (!sessionId.equals(httpServletRequest.getSession().getId())) {

                        log.info("-----session ---- token.sessionId={},http.session={}",sessionId, httpServletRequest.getSession().getId() );
                        throw new AuthenticationException("session不一致,请重新登录");
                    }*/
                }
                catch (Exception j) {
                    //异常token，从redis获取数据，看看是否存在数据
                    Object cacheObject = redisCacheUtils.getCacheObject(token);
                    if(cacheObject == null){
                        throw new AuthenticationException("token异常，请重新登录");
                    }
                }

                if (id == null) {
                    throw new AuthenticationException("非法数据，请重新登录");
                }


//                log.warn("AuthenticationInterceptor.preHandle tokenSessionId={},requestedsessionId={},sessionId={}",
//                        sessionId, httpServletRequest.getRequestedSessionId(), httpServletRequest.getSession().getId());
                // 如果请求中的sessionId与后台session中的sessionId不一致，退出
                /*if (!StringUtils.equals(httpServletRequest.getSession().getId(), httpServletRequest.getRequestedSessionId())) {
                    log.info("请求中的sessionId与后台session中的sessionId不一致！退出登录");
                    throw new AuthenticationException("非法数据，请重新登录");
                }*/
                if (JWTTokenUtil.isTokenEffect(token)) {
                    if (!JWTTokenUtil.updateTokenExpireTime(token)) {
                        log.info("更新token 有效时间失败！token={}",token);
                    }
                    log.info("更新token有效时间成功！token={}",token);
                }
                else {
                    log.info("token失效，请重新登录");
                    throw new AuthenticationException("登录过期或账号在其他地方登录，请重新登录");
                }
                //将id放入线程变量
                if (type.equals(UserType.MEMBER.toString())) {
//                    MemberDTO memberDTO = (MemberDTO) httpServletRequest.getSession().getAttribute(WebConst.SESSION_MEMBER);
//                    if (memberDTO == null) {
//                        // web 重启后  从session 取到的值可能是空
//                        MemberGetReq req = new MemberGetReq();
//                        req.setMemberId(id);
//                        memberDTO = memberService.getMember(req).getResultData();
//                    }
                    // 保存会员信息
//                    MemberContext.setMember(memberDTO);
                    MemberContext.setMemberId(id);
                    MemberContext.setUserSessionId(sessionId);
                }
                else if (type.equals(UserType.USER.toString())) {
                    BusUserResp userDTO = (BusUserResp) httpServletRequest.getSession().getAttribute(WebConst.SESSION_USER);
                    log.info("userDTO------------->{}", JSONObject.toJSONString(userDTO));

                    if (userDTO == null || !StringUtils.equals(id, userDTO.getUserId())) {
                        log.info("session里面的用户信息为空，或者 toke里面的用户ID和session里面的用户ID 不一样，重新获取用户信息");
                        userDTO = userService.getByUserId(id);
                        UserContext.setSysUser(userDTO);
                        UserContext.setUserId(id);
                        UserContext.setSessionId(sessionId);
//                        if (userDTO != null) {
//                            otherMsgDTO = saveUserOtherMsg(userDTO);
//                        }
                    }
                }
                this.setCustomResponseHeader(httpServletResponse);
                return true;
            }
        }
        else {
//            if(!checkSign(httpServletRequest)){
//                throw new AuthenticationException("非法请求，请检查参数");
//            }//TODO 前端添加签名跟时间戳后放开
            if (token == null || "".equals(token) || "null".equals(token) || "undefined".equals(token)) {
                return true;
            }
            try {
                Map<String, Claim> claims = JWT.decode(token).getClaims();
                id = claims.get("id").asString();
                sessionId = claims.get("sessionId").asString();
                type = claims.get("type").asString();
                if (StringUtils.isNotBlank(token) && JWTTokenUtil.isTokenEffect(token)) {
                    if (!JWTTokenUtil.updateTokenExpireTime(sessionId)) {
                        log.info("更新token 有效时间失败！token={}",token);
                    }
                    log.info("更新token 有效时间成功！token={}",token);
                }
            }
            catch (Exception j) {
                log.error("token异常", j);
            }

            //将id放入线程变量
            if (UserType.MEMBER.toString().equals(type)) {
//                MemberDTO memberDTO = (MemberDTO) httpServletRequest.getSession().getAttribute(WebConst.SESSION_MEMBER);
//                if (memberDTO == null) {
//                    // web 重启后  从session 取到的值可能是空
//                    MemberGetReq req = new MemberGetReq();
//                    req.setMemberId(id);
//                    memberDTO = memberService.getMember(req).getResultData();
//                }
//                // 保存会员信息
//                MemberContext.setMember(memberDTO);
                MemberContext.setMemberId(id);
                MemberContext.setUserSessionId(sessionId);
            }
            else if (UserType.USER.toString().equals(type)) {
                BusUserResp userDTO = (BusUserResp) httpServletRequest.getSession().getAttribute(WebConst.SESSION_USER);
                if (userDTO == null || !StringUtils.equals(id, userDTO.getUserId())) {
                    userDTO = userService.getByUserId(id);
                    UserContext.setSysUser(userDTO);
                    UserContext.setUserId(id);
                    UserContext.setSessionId(sessionId);
//                    if (userDTO != null) {
//                        otherMsgDTO = saveUserOtherMsg(userDTO);
//                    }
                }
            }
            this.setCustomResponseHeader(httpServletResponse);
            return true;
        }
        this.setCustomResponseHeader(httpServletResponse);
        return true;
    }


    /**
     * 自定义header返回信息
     * @param httpServletResponse
     */
    private void setCustomResponseHeader(HttpServletResponse httpServletResponse){
        log.info("header info userId: {}", UserContext.getUserId());
        httpServletResponse.setHeader("USER_ID", UserContext.getUserId());
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        //每次请求清除线程变量中的用户信息
        MemberContext.removeMemberSession();
        UserContext.removeUserSession();
    }

}
